package com.ruoyi.predict.service.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.predict.aiDomain.JobRecommendationResponse;
import com.ruoyi.predict.controller.PredictionController;
import com.ruoyi.predict.utils.JobRecommendationParser;
import com.ruoyi.predict.utils.JobRecommendationParser2;
import com.ruoyi.predict.utils.ScoreJsonParser;
import com.ruoyi.predict2.domain.BoleScriptPredictionRecord2;
import com.ruoyi.predict2.mapper.BoleScriptPredictionRecord2Mapper;
import com.ruoyi.resume.domain.BoleResumeEducation;
import com.ruoyi.resume.domain.BoleResumeProject;
import com.ruoyi.resume.domain.BoleResumeSkills;
import com.ruoyi.resume.domain.BoleResumeWork;
import com.ruoyi.resume.dto.ResumeDTO;
import com.ruoyi.resume.service.IResumeService;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.ollama.OllamaChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.predict.domain.BoleScriptPredictionRecord;
import com.ruoyi.predict.mapper.BoleScriptPredictionRecordMapper;
import com.ruoyi.predict.service.IPredictionService;

import javax.annotation.PreDestroy;

/**
 * 预测服务业务层处理
 * 
 * @author wujiale
 * @date 2024-07-12
 */
// 优化后的prompt模板，增加格式强制约束
//String prompt = "依据这份简历，推荐3个岗位！必须严格按照以下格式输出，不允许添加任何额外内容、标题或注释：\n" +
//        "1. 岗位名：[具体岗位名称]\n" +
//        "推荐理由：[一句话推荐理由]\n" +
//        "2. 岗位名：[具体岗位名称]\n" +
//        "推荐理由：[一句话推荐理由]\n" +
//        "3. 岗位名：[具体岗位名称]\n" +
//        "推荐理由：[一句话推荐理由]\n" +
//        "简历信息：技能有%s，教育程度有%s，工作经验有%s，专业有%s，目标岗位有%s";
@Service
public class PredictionServiceImpl implements IPredictionService {

    @Autowired
    private BoleScriptPredictionRecordMapper boleScriptPredictionRecordMapper;
    @Autowired
    private BoleScriptPredictionRecord2Mapper boleScriptPredictionRecordMapper2;
    @Autowired
    private IResumeService resumeService;

    private static final Logger logger = LoggerFactory.getLogger(PredictionController.class);

    // 单线程线程池，确保所有预测任务串行执行
    private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(runnable -> {
        Thread thread = new Thread(runnable);
        thread.setName("prediction-serial-thread"); // 命名线程便于调试
        thread.setDaemon(true); // 设为守护线程，避免阻塞应用关闭
        return thread;
    });

    @Log(title = "岗位预测", businessType = BusinessType.OTHER)
    @Override
    public Boolean predictJob() {
        Long userId = SecurityUtils.getUserId();

        // 将预测任务提交到单线程池，确保串行执行
        CompletableFuture.runAsync(() -> {executePrediction(userId); executeResumeAnalysis(userId);}, singleThreadExecutor)

                .exceptionally(ex -> {
                    logger.error("用户[{}]岗位预测任务执行异常", userId, ex);
                    return null;
                });

        return true; // 返回true表示任务已提交，而非执行完成
    }


    // 实际执行预测逻辑的方法
    private void executePrediction(Long userId) {
        try {
            BoleScriptPredictionRecord predictionRecord = boleScriptPredictionRecordMapper.selectBoleScriptPredictionRecordByUserId(userId);
            ResumeDTO boleResumes = resumeService.getResume(userId);

            if (boleResumes == null) {
                logger.warn("用户[{}]未找到简历信息，无法进行岗位预测", userId);
                return;
            }

            // 构建prompt（基于实际实体类结构）
            // 1. 处理技能（BoleResumeSkills列表，提取skillName）
            StringBuilder skillsSb = new StringBuilder();
            if (boleResumes.getSkills() != null && !boleResumes.getSkills().isEmpty()) {
                for (BoleResumeSkills skill : boleResumes.getSkills()) {
                    if (skill != null && skill.getSkillName() != null) {
                        if (skillsSb.length() > 0) {
                            skillsSb.append("、"); // 用顿号分隔多个技能
                        }
                        skillsSb.append(skill.getSkillName());
                    }
                }
            }
            String skills = skillsSb.length() > 0 ? skillsSb.toString() : "无";

            // 2. 处理教育经历（BoleResumeEducation列表，拼接学历、学校、专业）
            StringBuilder educationSb = new StringBuilder();
            if (boleResumes.getEducations() != null && !boleResumes.getEducations().isEmpty()) {
                for (BoleResumeEducation edu : boleResumes.getEducations()) {
                    if (edu != null) {
                        // 跳过空的教育记录
                        if (edu.getDegree() == null && edu.getSchool() == null) {
                            continue;
                        }
                        // 拼接格式：学历（学校，专业）
                        StringBuilder eduItem = new StringBuilder();
                        if (edu.getDegree() != null) {
                            eduItem.append(edu.getDegree());
                        }
                        if (edu.getSchool() != null) {
                            eduItem.append("（").append(edu.getSchool());
                            // 补充专业（如果有）
                            if (edu.getMajor() != null) {
                                eduItem.append("，").append(edu.getMajor());
                            }
                            eduItem.append("）");
                        }
                        // 多条教育经历用分号分隔
                        if (educationSb.length() > 0) {
                            educationSb.append("；");
                        }
                        educationSb.append(eduItem);
                    }
                }
            }
            String education = educationSb.length() > 0 ? educationSb.toString() : "无";

            // 3. 处理工作经验
            String experience = "无";
            if (boleResumes.getExperience() != null) {
                experience = boleResumes.getExperience();
            } else if (boleResumes.getWorkExperiences() != null && !boleResumes.getWorkExperiences().isEmpty()) {
                StringBuilder expSb = new StringBuilder();
                for (BoleResumeWork work : boleResumes.getWorkExperiences()) {
                    if (work != null && work.getPosition() != null) {
                        if (expSb.length() > 0) {
                            expSb.append("；");
                        }
                        expSb.append(work.getPosition());
                        if (work.getCompany() != null) {
                            expSb.append("（").append(work.getCompany()).append("）");
                        }
                    }
                }
                experience = expSb.length() > 0 ? expSb.toString() : "无";
            }

            // 4. 处理专业（从第一条教育经历中提取，若存在）
            String major = "无";
            if (boleResumes.getEducations() != null && !boleResumes.getEducations().isEmpty()) {
                BoleResumeEducation firstEdu = boleResumes.getEducations().get(0);
                if (firstEdu != null && firstEdu.getMajor() != null) {
                    major = firstEdu.getMajor();
                }
            }

            // 5. 处理目标岗位
            String target = boleResumes.getJobTarget() != null ? boleResumes.getJobTarget() : "无";

            // 6. 处理项目经验（新增）
            StringBuilder projectsSb = new StringBuilder();
            if (boleResumes.getProjects() != null && !boleResumes.getProjects().isEmpty()) {
                int projectIndex = 1;
                for (BoleResumeProject project : boleResumes.getProjects()) {
                    if (project != null && project.getName() != null) {
                        // 项目序号
                        projectsSb.append("项目").append(projectIndex).append("：");

                        // 项目名称
                        projectsSb.append(project.getName());

                        // 角色（如果有）
                        if (project.getRole() != null) {
                            projectsSb.append("，担任").append(project.getRole());
                        }

                        // 主要成就（如果有）
                        if (project.getAchievements() != null) {
                            projectsSb.append("，成就：").append(project.getAchievements());
                        }

                        // 多个项目用分号分隔
                        projectsSb.append("；");
                        projectIndex++;
                    }
                }
                // 移除最后一个分号
                if (projectsSb.length() > 0) {
                    projectsSb.setLength(projectsSb.length() - 1);
                }
            }
            String projects = projectsSb.length() > 0 ? projectsSb.toString() : "无";

            // 7. 构建最终prompt（已添加项目经验）
            String prompt = "依据这份简历，推荐3个岗位！无需额外分析，必须给出(岗位名:xxx，推荐理由:xxx)，必须严格按照以下格式输出，不允许添加任何额外内容、标题或注释！格式：\n" +
                    "1. 岗位名：xxx 推荐理由\n" +
                    "2. 岗位名：xxx 推荐理由\n" +
                    "3. 岗位名：xxx 推荐理由\n" +
                    "简历信息：技能有%s，教育程度有%s，工作经验有%s，专业有%s，目标岗位有%s，项目经验有%s";

            // 格式化拼接
            prompt = String.format(prompt, skills, education, experience, major, target, projects);
            logger.info("用户[{}]的预测prompt: {}", userId, prompt);

            // 以下代码保持不变...
            OllamaChatModel model = OllamaChatModel.builder()
//                    .baseUrl("http://www.bigpigbig.cn:11434")
                    .baseUrl("https://api.modelarts-maas.com/v1/chat/completions")
                    .modelName("qwen:1.8b")
                    .temperature(0.1)
                    .timeout(Duration.ofSeconds(120))
                    .numPredict(300)
                    .build();

            ChatMessage userMessage = UserMessage.from(prompt);
            String rawPredictionResult = String.valueOf(model.generate(Collections.singletonList(userMessage)).content());
            String remark=rawPredictionResult;
            String predictionResult;
            JobRecommendationResponse jobRecommendationResponse = new JobRecommendationResponse();
            try{
                jobRecommendationResponse = JobRecommendationParser.parse(rawPredictionResult);
                if (jobRecommendationResponse != null && jobRecommendationResponse.getRecommendations() != null) {
                    predictionResult = JSON.toJSONString(jobRecommendationResponse.getRecommendations());
                    logger.info("用户[{}]解析后JSON: {}", userId, predictionResult);
                } else {
                    predictionResult = rawPredictionResult;
                    logger.warn("用户[{}]所有解析策略失败，使用原始文本", userId);
                }
            }catch (Exception e){
                predictionResult = rawPredictionResult;
                logger.error("用户[{}]岗位预测失败", userId, e);
            }

            // 保存预测结果
            if (predictionRecord == null) {
                predictionRecord = new BoleScriptPredictionRecord();
                predictionRecord.setUserId(userId);
                predictionRecord.setPredictionTime(new Date());
                predictionRecord.setPredictionResult(predictionResult);
                predictionRecord.setPredictionInfo(prompt);
                predictionRecord.setRemark(remark);
                boleScriptPredictionRecordMapper.insertBoleScriptPredictionRecord(predictionRecord);
            } else {
                predictionRecord.setPredictionResult(predictionResult);
                predictionRecord.setPredictionInfo(prompt);
                predictionRecord.setRemark(remark);
                predictionRecord.setPredictionTime(new Date());
                boleScriptPredictionRecordMapper.updateBoleScriptPredictionRecord(predictionRecord);
            }

            logger.info("用户[{}]岗位预测完成", userId);

        } catch (Exception e) {
            logger.error("用户[{}]岗位预测失败", userId, e);
        }
    }
    public void  executeResumeAnalysis(Long userId){
        try {
            BoleScriptPredictionRecord2 predictionRecord = boleScriptPredictionRecordMapper2.selectBoleScriptPredictionRecordByUserId(userId);
            ResumeDTO boleResumes = resumeService.getResume(userId);

            if (boleResumes == null) {
                logger.warn("用户[{}]未找到简历信息，无法进行岗位预测", userId);
                return;
            }

            // 构建prompt（基于实际实体类结构）
            // 1. 处理技能（BoleResumeSkills列表，提取skillName）
            StringBuilder skillsSb = new StringBuilder();
            if (boleResumes.getSkills() != null && !boleResumes.getSkills().isEmpty()) {
                for (BoleResumeSkills skill : boleResumes.getSkills()) {
                    if (skill != null && skill.getSkillName() != null) {
                        if (skillsSb.length() > 0) {
                            skillsSb.append("、"); // 用顿号分隔多个技能
                        }
                        skillsSb.append(skill.getSkillName());
                    }
                }
            }
            String skills = skillsSb.length() > 0 ? skillsSb.toString() : "无";

            // 2. 处理教育经历（BoleResumeEducation列表，拼接学历、学校、专业）
            StringBuilder educationSb = new StringBuilder();
            if (boleResumes.getEducations() != null && !boleResumes.getEducations().isEmpty()) {
                for (BoleResumeEducation edu : boleResumes.getEducations()) {
                    if (edu != null) {
                        // 跳过空的教育记录
                        if (edu.getDegree() == null && edu.getSchool() == null) {
                            continue;
                        }
                        // 拼接格式：学历（学校，专业）
                        StringBuilder eduItem = new StringBuilder();
                        if (edu.getDegree() != null) {
                            eduItem.append(edu.getDegree());
                        }
                        if (edu.getSchool() != null) {
                            eduItem.append("（").append(edu.getSchool());
                            // 补充专业（如果有）
                            if (edu.getMajor() != null) {
                                eduItem.append("，").append(edu.getMajor());
                            }
                            eduItem.append("）");
                        }
                        // 多条教育经历用分号分隔
                        if (educationSb.length() > 0) {
                            educationSb.append("；");
                        }
                        educationSb.append(eduItem);
                    }
                }
            }
            String education = educationSb.length() > 0 ? educationSb.toString() : "无";

            // 3. 处理工作经验
            String experience = "无";
            if (boleResumes.getExperience() != null) {
                experience = boleResumes.getExperience();
            } else if (boleResumes.getWorkExperiences() != null && !boleResumes.getWorkExperiences().isEmpty()) {
                StringBuilder expSb = new StringBuilder();
                for (BoleResumeWork work : boleResumes.getWorkExperiences()) {
                    if (work != null && work.getPosition() != null) {
                        if (expSb.length() > 0) {
                            expSb.append("；");
                        }
                        expSb.append(work.getPosition());
                        if (work.getCompany() != null) {
                            expSb.append("（").append(work.getCompany()).append("）");
                        }
                    }
                }
                experience = expSb.length() > 0 ? expSb.toString() : "无";
            }

            // 4. 处理专业（从第一条教育经历中提取，若存在）
            String major = "无";
            if (boleResumes.getEducations() != null && !boleResumes.getEducations().isEmpty()) {
                BoleResumeEducation firstEdu = boleResumes.getEducations().get(0);
                if (firstEdu != null && firstEdu.getMajor() != null) {
                    major = firstEdu.getMajor();
                }
            }

            // 5. 处理目标岗位
            String target = boleResumes.getJobTarget() != null ? boleResumes.getJobTarget() : "无";

            // 6. 处理项目经验（新增）
            StringBuilder projectsSb = new StringBuilder();
            if (boleResumes.getProjects() != null && !boleResumes.getProjects().isEmpty()) {
                int projectIndex = 1;
                for (BoleResumeProject project : boleResumes.getProjects()) {
                    if (project != null && project.getName() != null) {
                        // 项目序号
                        projectsSb.append("项目").append(projectIndex).append("：");

                        // 项目名称
                        projectsSb.append(project.getName());

                        // 角色（如果有）
                        if (project.getRole() != null) {
                            projectsSb.append("，担任").append(project.getRole());
                        }

                        // 主要成就（如果有）
                        if (project.getAchievements() != null) {
                            projectsSb.append("，成就：").append(project.getAchievements());
                        }

                        // 多个项目用分号分隔
                        projectsSb.append("；");
                        projectIndex++;
                    }
                }
                // 移除最后一个分号
                if (projectsSb.length() > 0) {
                    projectsSb.setLength(projectsSb.length() - 1);
                }
            }
            String projects = projectsSb.length() > 0 ? projectsSb.toString() : "无";

            // 7. 构建最终prompt（已添加项目经验）
            String prompt = "依据这份简历，给这个人打分！无需额外分析，必须给出分数，分数范围0-100，最低60分，必须严格按照以下格式输出，不允许添加任何额外内容、标题或注释！格式：\n" +
                    "1. 学习能力：xx分\n" +
                    "2. 实战能力：xx分\n" +
                    "3. 沟通协调能力：xx分\n" +
                    "4. 成就动机：xx分\n" +
                    "简历信息：技能有%s，教育程度有%s，工作经验有%s，专业有%s，目标岗位有%s，项目经验有%s";

            // 格式化拼接
            prompt = String.format(prompt, skills, education, experience, major, target, projects);
            logger.info("用户[{}]的预测prompt: {}", userId, prompt);

            // 以下代码保持不变...
            OllamaChatModel model = OllamaChatModel.builder()
                    .baseUrl("http://www.bigpigbig.cn:11434")
                    .modelName("qwen:1.8b")
                    .temperature(0.1)
                    .timeout(Duration.ofSeconds(120))
                    .numPredict(300)
                    .build();

            ChatMessage userMessage = UserMessage.from(prompt);
            String rawPredictionResult = String.valueOf(model.generate(Collections.singletonList(userMessage)).content());
            String remark=rawPredictionResult;

            System.out.println(remark);

            try{
               String  rawPredictionResult1 = ScoreJsonParser.parseToJson(rawPredictionResult);
                rawPredictionResult = rawPredictionResult1;
            }catch (Exception e){

                logger.error("用户[{}]岗位预测失败", userId, e);
            }
            System.out.println(rawPredictionResult);

            // 保存预测结果
            if (predictionRecord == null) {
                predictionRecord = new BoleScriptPredictionRecord2();
                predictionRecord.setUserId(userId);
                predictionRecord.setPredictionTime(new Date());
                predictionRecord.setPredictionResult(rawPredictionResult);
                predictionRecord.setPredictionInfo("岗位预测信息");
                predictionRecord.setRemark(remark);
                boleScriptPredictionRecordMapper2.insertBoleScriptPredictionRecord2(predictionRecord);
            } else {
                predictionRecord.setPredictionResult(rawPredictionResult);
                predictionRecord.setPredictionInfo(prompt);
                predictionRecord.setRemark(remark);
                predictionRecord.setPredictionTime(new Date());
                boleScriptPredictionRecordMapper2.updateBoleScriptPredictionRecord2(predictionRecord);
            }
            logger.info("用户[{}]岗位预测完成", userId);

        } catch (Exception e) {
            logger.error("用户[{}]岗位预测失败", userId, e);
        }
    }
    // 应用关闭时关闭线程池，释放资源
    @PreDestroy
    public void destroy() {
        if (!singleThreadExecutor.isShutdown()) {
            singleThreadExecutor.shutdown();
            logger.info("预测任务单线程池已关闭");
        }
    }
    /**
     * 调用Python预测脚本进行岗位预测
     * 
     * @param skills 技能列表，逗号分隔
     * @param education 教育程度
     * @param target 目标岗位
     * @param major 专业
     * @return 预测记录对象
     */
    @Override
    public BoleScriptPredictionRecord callPredictionScript(String skills, String education, String experience, String major, String target) {
        // 参数校验
        if (StringUtils.isEmpty(skills) || StringUtils.isEmpty(education) || 
            StringUtils.isEmpty(target) || StringUtils.isEmpty(major)) {
            throw new IllegalArgumentException("预测参数不能为空");
        }

        StringBuilder result = new StringBuilder();
        ProcessBuilder processBuilder = new ProcessBuilder();
        BoleScriptPredictionRecord predictionRecord = new BoleScriptPredictionRecord();

        try {
            // 生成预测单号
            String predictionNo = "PRED" + DateUtils.dateTimeNow("yyyyMMdd") + UUID.randomUUID().toString().substring(0, 5);

            // 设置命令和参数
            processBuilder.command(
                "python", 
                "e:\\xit.edu.cn\\bole\\RuoYi\\RuoYi-Vue-master\\bole-resume\\recommendation-algorithm\\scripts\\single_student_prediction.py",
                "--skills", skills,
                "--education", education,
                "--target", target,
                "--major", major,
                "--experience", experience
            );

            // 启动进程
            Process process = processBuilder.start();

            // 读取输出，使用UTF-8编码
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
            System.out.println(result.toString());
            System.out.println("wujiale");
            // 等待进程完成
            int exitCode = process.waitFor();
            System.out.println(exitCode);
            // 创建预测记录
            predictionRecord.setUserId(SecurityUtils.getUserId());
            predictionRecord.setPredictionNo(predictionNo);
            predictionRecord.setPredictionJob("未知"); // 实际应用中从脚本输出解析
            predictionRecord.setPredictionJobCode("UNKNOWN"); // 实际应用中从脚本输出解析
            predictionRecord.setPredictionInfo(String.format("{\"skills\":\"%s\",\"education\":\"%s\",\"target\":\"%s\",\"major\":\"%s\"}",
                skills, education, target, major));
//            predictionRecord.setPredictionResult(result);
            predictionRecord.setPredictionTime(new Date());
            predictionRecord.setCreateTime(DateUtils.getNowDate());
            predictionRecord.setUpdateTime(DateUtils.getNowDate());

            // 保存到数据库
            boleScriptPredictionRecordMapper.insertBoleScriptPredictionRecord(predictionRecord);

        } catch (IOException | InterruptedException e) {
            throw new RuntimeException("调用预测脚本失败: " + e.getMessage(), e);
        }

        return predictionRecord;
    }
}